package de.chessgame.gui;

import java.util.ArrayList;
import java.util.Iterator;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;

import com.google.common.collect.BiMap;

import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import de.chessgame.ChessGameMod;
import de.chessgame.gamelogic.Board;
import de.chessgame.gamelogic.Coordinate;
import de.chessgame.gamelogic.piece.Fraction;
import de.chessgame.gamelogic.piece.Piece;
import de.chessgame.inventory.ContainerChessBoard;
import de.chessgame.network.ChessMessage;
import de.chessgame.tileentity.TileEntityChessBoard;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiLabel;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;

@SideOnly(Side.CLIENT)
public class GuiChessBoard extends GuiContainer {

	private static ResourceLocation guiChessTexture = new ResourceLocation(
			ChessGameMod.MODID + ":"
					+ "textures/gui/container/guichesstexture.png");
	private TileEntityChessBoard tileChess;
	private EntityPlayer player;
	private boolean white;
	public ArrayList<Coordinate> highlights;
	private GuiButton resetGame;
	private GuiButton lockUnlock;
	private GuiButton queen;
	private GuiButton rook;
	private GuiButton knight;
	private GuiButton bishop;
	private boolean pieceSelectionOpened;
	private int xSizePieceSelection;
	private int ySizePieceSelection;
	private int xSizeMessages;
	private int ySizeMessages;

	/** Sizes GUI: **/
	private static final int CORNER_WIDTH = 3;
	private static final int SPACE_AT_RIGHT = 96;
	private static final int HORIZONTAL_SPACE_BETWEEN_BUTTONS = 8;
	private static final int BUTTON_WIDTH = 80;
	private static final int BUTTON_HEIGHT = 20;

	private static final int MARGIN = 8;

	/** Chessboard: **/
	private static final int BOARD_SQUARE_COUNT = 8;
	private static final int BOARD_LEFT = 8;
	private static final int BOARD_TOP = 8;
	private static final int SQUARE_SIZE = 16;

	/** Button-ids: **/
	public static final int RESET_GAME = 0;
	public static final int LOCK_UNLOCK = 1;
	public static final int QUEEN = 0x002;
	public static final int ROOK = 0x102;
	public static final int KNIGHT = 0x202;
	public static final int BISHOP = 0x302;
	/** button 2 used **/

	public static final int GUI_CLOSED = 0;
	public static final int CLEAR_ITEMSTACK = 1;

	private static final int SPACE = 12;
	private static final int BUTTON_WIDTH_PIECE_SELECTION = 16;
	private static final int BUTTON_HEIGHT_PIECE_SELECTION = 16;

	public GuiChessBoard(EntityPlayer player,
			TileEntityChessBoard tileEntityChessBoard) {
		super(new ContainerChessBoard(player, tileEntityChessBoard));
		this.player = player;
		this.tileChess = tileEntityChessBoard;
		this.xSize = 236;
		this.ySize = 144;
		this.xSizePieceSelection = 80;
		this.ySizePieceSelection = 32;
		this.xSizeMessages = 128;
		this.ySizeMessages = 48;
		this.pieceSelectionOpened = false;
		this.white = !(player.getDisplayName().equals(tileChess
				.getPlayerOnBlack()));
	}

	@Override
	protected void drawGuiContainerForegroundLayer(int par1, int par2) {
		// heading
		String name = this.tileChess.hasCustomInventoryName() ? tileChess
				.getInventoryName() : I18n.format(StatCollector
				.translateToLocal(this.tileChess.getInventoryName()),
				new Object[0]);
		this.fontRendererObj.drawString(name, this.xSize
				- (this.fontRendererObj.getStringWidth(name) + SPACE_AT_RIGHT)
				/ 2 - CORNER_WIDTH, 8, 4210752);

		// highlights
		highlights = tileChess.getHighlights();
		highlightSquares(highlights);

		// lock:
		String info = "";
		boolean locked = false;

		if (tileChess.isWhiteSlotLocked()) {
			if (white) {
				locked = true;
			}
			info += StatCollector.translateToLocal("gui.whiteslot") + ":"
					+ "\n" + "  " + tileChess.getPlayerOnWhiteLock() + "\n";
		}

		if (tileChess.isBlackSlotLocked()) {
			if (!white) {
				locked = true;
			}
			info += StatCollector.translateToLocal("gui.blackslot") + ":"
					+ "\n" + "  " + tileChess.getPlayerOnBlackLock() + "\n";
		}

		lockUnlock.displayString = StatCollector
				.translateToLocal(locked ? "gui.unlockslot" : "gui.lockslot");

		drawStringMultipleLines(info, this.xSize - SPACE_AT_RIGHT, 8 + SPACE,
				4210752);

		// Playerturn
		this.drawRect(this.xSize - 24, 8 + 5 * SPACE, this.xSize - 8,
				8 + 5 * SPACE + 16,
				tileChess.isWhitePlayerOnTurn() ? 0xffffffff : 0xff000000);
		this.fontRendererObj.drawString(
				StatCollector.translateToLocal("gui.playeronturn") + ": ",
				this.xSize - SPACE_AT_RIGHT, 8 + 5 * SPACE, 4210752);
		
		if (tileChess.isGameFinished()) {
			drawEnd();
		}
	}

	private void drawEnd() {
		GL11.glColor4f(1f, 1.0f, 1.0f, 1f);
		int left = BOARD_LEFT
				+ (xSize - xSizeMessages) / 2;
		int top = ySize;
		this.mc.getTextureManager().bindTexture(guiChessTexture);
		this.drawTexturedModalRect(left, top, xSizePieceSelection, ySize,
				xSizeMessages, ySizeMessages);

		String unlocalizedString = "";

		switch (tileChess.getTypeOfEnding()) {
		case Board.DRAW:
			unlocalizedString = "gui.ending.draw";
			break;
			
		case Board.WHITE_PLAYER_WINS:
			unlocalizedString = "gui.ending.whiteplayerwins";
			break;
			
		case Board.BLACK_PLAYER_WINS:
			unlocalizedString = "gui.ending.blackplayerwins";
			break;

		default:
			break;
		}
		String string = StatCollector.translateToLocal(unlocalizedString);
		this.fontRendererObj.drawString(string, left
				+ (xSizeMessages - this.fontRendererObj.getStringWidth(string))
				/ 2, top
				+ 18, 0x00000000);
	}

	private void openPieceSelection() {
		pieceSelectionOpened = true;
		setPieceSelectionButtonsVisible(true);
	}

	private void closePieceSelection() {
		pieceSelectionOpened = false;
		setPieceSelectionButtonsVisible(false);
	}

	private void drawStringMultipleLines(String string, int x, int y, int color) {
		String modifiedString = "";
		char[] charArray = string.toCharArray();

		for (int i = 0, j = 0; i < charArray.length; i++) {
			if (charArray[i] == '\n') {
				this.fontRendererObj.drawString(modifiedString, x, y + SPACE
						* j, color);
				modifiedString = "";
				j++;
			} else {
				modifiedString += charArray[i];
			}
		}
	}

	private void highlightSquares(ArrayList<Coordinate> highlights) {
		int x;
		int y;
		if (white) {
			for (Coordinate highlight : highlights) {
				x = highlight.x * SQUARE_SIZE + BOARD_LEFT;
				y = highlight.y * SQUARE_SIZE + BOARD_TOP;
				this.drawRect(x, y, x + SQUARE_SIZE, y + SQUARE_SIZE,
						0xafdf0000);
			}
		} else {
			for (Coordinate highlight : highlights) {
				x = (BOARD_SQUARE_COUNT - highlight.x - 1) * SQUARE_SIZE
						+ BOARD_LEFT;
				y = (BOARD_SQUARE_COUNT - highlight.y - 1) * SQUARE_SIZE
						+ BOARD_TOP;
				this.drawRect(x, y, x + SQUARE_SIZE, y + SQUARE_SIZE,
						0xafdf0000);
			}
		}
	}

	@Override
	protected void drawGuiContainerBackgroundLayer(float par1, int par2,
			int par3) {
		GL11.glColor4f(1.0f, 1.0f, 1.0f, 1f);
		guiLeft = (width - xSize) / 2;
		guiTop = (height - ySize) / 2;
		this.mc.getTextureManager().bindTexture(guiChessTexture);
		this.drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize);

		if (!pieceSelectionOpened && tileChess.hasUndefined()) {
			openPieceSelection();
		} else if (pieceSelectionOpened && !tileChess.hasUndefined()) {
			closePieceSelection();
		}

		if (pieceSelectionOpened) {
			drawPieceSelection();
		}
	}

	private void drawPieceSelection() {
		int left = guiLeft + xSize;
		int top = guiTop + 8;
		this.mc.getTextureManager().bindTexture(guiChessTexture);
		this.drawTexturedModalRect(left, top, 0, ySize,
				xSizePieceSelection, ySizePieceSelection);
	}

	@Override
	public void initGui() {
		super.initGui();
		resetGame = createGuiButton(RESET_GAME, "gui.resetgame");
		lockUnlock = createGuiButton(LOCK_UNLOCK, "gui.lockslot");
		queen = createGuiButtonPieceSelection(QUEEN, "gui.queen");
		rook = createGuiButtonPieceSelection(ROOK, "gui.rook");
		knight = createGuiButtonPieceSelection(KNIGHT, "gui.knight");
		bishop = createGuiButtonPieceSelection(BISHOP, "gui.bishop");
		setPieceSelectionButtonsVisible(false);
		buttonList.add(resetGame);
		buttonList.add(lockUnlock);
		buttonList.add(queen);
		buttonList.add(rook);
		buttonList.add(knight);
		buttonList.add(bishop);
	}

	private GuiButton createGuiButton(int id, String unlocalizedString) {
		int xPosition = guiLeft + xSize - (BUTTON_WIDTH + SPACE_AT_RIGHT) / 2
				- CORNER_WIDTH;
		int yPosition = guiTop + ySize
				- (HORIZONTAL_SPACE_BETWEEN_BUTTONS + BUTTON_HEIGHT) * id + 1
				- BUTTON_HEIGHT - 8 - CORNER_WIDTH;
		return new GuiButton(id, xPosition, yPosition, BUTTON_WIDTH,
				BUTTON_HEIGHT,
				StatCollector.translateToLocal(unlocalizedString));
	}

	private GuiButton createGuiButtonPieceSelection(int buttonId,
			String unlocalizedString) {
		int id = buttonId >> 8;
		int xPosition = guiLeft + xSize + MARGIN + id
				* BUTTON_WIDTH_PIECE_SELECTION;
		int yPosition = guiTop + 8 + MARGIN;
		return new GuiButton(buttonId, xPosition, yPosition,
				BUTTON_WIDTH_PIECE_SELECTION, BUTTON_HEIGHT_PIECE_SELECTION,
				StatCollector.translateToLocal(unlocalizedString));
	}

	@Override
	protected void actionPerformed(GuiButton guiButton) {
		super.actionPerformed(guiButton);
		sendEventToServer(true, guiButton.id);
		if (pieceSelectionOpened) {
			closePieceSelection();
		}
	}

	private void sendEventToServer(boolean button, int data) {
		int x = tileChess.xCoord;
		int y = tileChess.yCoord;
		int z = tileChess.zCoord;
		ChessGameMod.simpleNetworkWrapper.sendToServer(new ChessMessage(button,
				data, x, y, z));
	}

	// Ignore clicks outside gui

	@Override
	protected void mouseMovedOrUp(int x, int y, int which) {

		if (mouseIsOnActiveGui(x, y)) {
			if (which != 0) {
				super.mouseMovedOrUp(x, y, which);
				return;
			}

			Slot slot = this.getSlotAtPosition(x, y);

			if (slot == null) {
				super.mouseMovedOrUp(x, y, which);
				return;
			}

			if (tileChess.isGameFinished()) {
				return;
			}

			ItemStack itemStack = this.mc.thePlayer.inventory.getItemStack();

			if (slot.getHasStack()) {
				if (itemStack == null) {
					/**
					 * Because of this, a player only can move his own pieces on
					 * his turn.
					 **/
					if (tileChess.isSlotUsableByPlayer(slot.getSlotIndex(),
							player)) {
						super.mouseMovedOrUp(x, y, which);
					}
				} else {
					if (isSlotHighlighted(slot.getSlotIndex())) {
						super.mouseMovedOrUp(x, y, which);
						if (itemStack != null) {
							if (player.inventory.getItemStack() != null) {
								player.inventory.setItemStack(null);
							}
							sendEventToServer(false, CLEAR_ITEMSTACK);
						}
					}
				}
			} else { // slot.hasStack == false
				if (itemStack == null) {
					super.mouseMovedOrUp(x, y, which);
				} else { // itemStack != null
					if (isSlotHighlighted(slot.getSlotIndex())) {
						super.mouseMovedOrUp(x, y, which);
					}
				}
			}
		}
	}

	@Override
	protected void mouseClicked(int x, int y, int button) {
		if (mouseIsOnActiveGui(x, y)) {
			if (button != 0) {
				super.mouseClicked(x, y, button);
				return;
			}

			Slot slot = this.getSlotAtPosition(x, y);

			if (slot == null) {
				super.mouseClicked(x, y, button);
				return;
			}

			if (tileChess.isGameFinished()) {
				return;
			}

			ItemStack itemStack = this.mc.thePlayer.inventory.getItemStack();

			if (slot.getHasStack()) {
				if (itemStack == null) {
					/**
					 * Because of this, a player only can move his own pieces on
					 * his turn.
					 **/
					if (tileChess.isSlotUsableByPlayer(slot.getSlotIndex(),
							player)) {
						super.mouseClicked(x, y, button);
					}
				} else {
					if (isSlotHighlighted(slot.getSlotIndex())) {
						super.mouseClicked(x, y, button);
					}
				}
			} else { // slot.hasStack == false
				if (itemStack == null) {
					super.mouseClicked(x, y, button);
				} else { // itemStack != null
					if (isSlotHighlighted(slot.getSlotIndex())) {
						super.mouseClicked(x, y, button);
					}
				}
			}
		}
	}

	private void setPieceSelectionButtonsVisible(boolean visible) {
		queen.visible = visible;
		rook.visible = visible;
		knight.visible = visible;
		bishop.visible = visible;
	}

	private boolean isSlotHighlighted(int number) {
		for (Coordinate coordinate : highlights) {
			if (number == TileEntityChessBoard.getSlotIdFromCoordinate(
					coordinate.x, coordinate.y)) {
				return true;
			}
		}
		return false;
	}

	private boolean mouseIsOnActiveGui(int x, int y) {
		int guiStartX = guiLeft;
		int guiStartY = guiTop;
		int guiEndX = guiLeft + xSize;
		int guiEndY = guiTop + ySize;
		if (pieceSelectionOpened) {
			int guiStartX2 = guiEndX;
			int guiStartY2 = guiTop + 8;
			int guiEndX2 = guiStartX2 + xSizePieceSelection;
			int guiEndY2 = guiStartY2 + ySizePieceSelection;
			return x >= guiStartX2 && x < guiEndX2 && y >= guiStartY2
					&& y < guiEndY2;
		} else {
			return x >= guiStartX && x < guiEndX && y >= guiStartY
					&& y < guiEndY;
		}
	}

	/**
	 * Returns the slot at the given coordinates or null if there is none. (Copy
	 * from super-class)
	 */
	private Slot getSlotAtPosition(int x, int y) {
		for (int k = 0; k < this.inventorySlots.inventorySlots.size(); ++k) {
			Slot slot = (Slot) this.inventorySlots.inventorySlots.get(k);

			if (this.isMouseOverSlot(slot, x, y)) {
				return slot;
			}
		}

		return null;
	}

	/**
	 * Returns if the passed mouse position is over the specified slot. (Copy
	 * from super-class)
	 */
	private boolean isMouseOverSlot(Slot slot, int x, int y) {
		return this.func_146978_c(slot.xDisplayPosition, slot.yDisplayPosition,
				SQUARE_SIZE, SQUARE_SIZE, x, y);
	}

	@Override
	public void onGuiClosed() {
		if (!(player.openContainer instanceof ContainerChessBoard)) {
			if (player.inventory.getItemStack() != null) {
				player.inventory.setItemStack(null);
			}

			super.onGuiClosed();
			sendEventToServer(false, GUI_CLOSED);
		}
	}
}